package fwmpc.service.impl.basic;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import frpc.manager.SimpleRedisBaskManager;
import fwmpc.exception.WeiXinRuntimeException;
import fwmpc.function.WeiXinTokenManagerFunction;
import fwmpc.interact.IWeiXinInteraction;
import fwmpc.resource.IWeiXinAttributeItem;
import fwmpc.service.IWeiXinManagerService;
import fwmpc.store.IWeiXinAttributeStore;
import fwmpc.utils.WeiXinToolUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.util.MultiValueMap;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 类描述:  微信业务处理基类基础对象
 *
 * @author 王雷
 * @version 1.0.0
 * @date 2021/1/21 0021 下午 10:15
 */
@Slf4j
public abstract class AbstractWeiXinBasicService extends SimpleRedisBaskManager implements IWeiXinManagerService {

    /**
     * 属性描述：微信通讯配置对象
     *
     * @date : 2021/1/21 0021 下午 10:17
     */
    protected final IWeiXinInteraction iWeiXinInteraction;

    /**
     * 属性描述：微信属性存储对象
     *
     * @date : 2021/1/21 0021 下午 10:18
     */
    protected final IWeiXinAttributeStore iWeiXinAttributeStore;

    /**
     * 功能描述：微信业务处理基类基础对象
     *
     * @param iWeiXinInteraction    微信通讯配置对象
     * @param iWeiXinAttributeStore 微信属性存储对象
     * @author : 王雷
     * @date : 2021/1/21 0021 下午 10:19
     */
    public AbstractWeiXinBasicService(IWeiXinInteraction iWeiXinInteraction
            , IWeiXinAttributeStore iWeiXinAttributeStore) {
        this.iWeiXinInteraction = iWeiXinInteraction;
        this.iWeiXinAttributeStore = iWeiXinAttributeStore;
    }

    @Override
    public String giveDefaultAppid() {
        return iWeiXinAttributeStore.giveWeiXinCommonAttributeDefault().giveAppId();
    }

    /**
     * 功能描述：根据AppId获取对应的属性描述对象
     *
     * @date : 2022/6/28 0028 下午 3:34
     */
    protected IWeiXinAttributeItem giveWeiXinAttributeItem(String appId) throws WeiXinRuntimeException {
        if (!StringUtils.isNotEmpty(appId)) {
            return iWeiXinAttributeStore.giveWeiXinCommonAttributeDefault();
        }
        return iWeiXinAttributeStore.giveWeiXinCommonAttribute(appId);
    }

    /**
     * 功能描述：根据AppId获取对应的属性描述对象
     *
     * @date : 2022/6/28 0028 下午 3:34
     */
    protected IWeiXinAttributeItem giveWeiXinAttributeItem() throws WeiXinRuntimeException {
        return iWeiXinAttributeStore.giveWeiXinCommonAttributeDefault();
    }

    /**
     * 功能描述：微信参数——平台标识
     *
     * @author : 王雷
     * @date : 2021/1/16 0016 下午 9:05
     */
    @Override
    public Long giveAppPlatformId(String appId) {
        return giveWeiXinAttributeItem(appId).giveAppPlatformId();
    }

    /**
     * 功能描述：根据AppId获取Mch信息
     *
     * @param appId           标识
     * @param comparisonValue 比较值
     * @throws Exception 处理异常
     * @author : 王雷
     * @date : 2021/1/21 0021 上午 11:37
     */
    @Override
    public <D> IWeiXinAttributeItem.MchDataBean giveMchDataByAppIdToMap(String appId, D comparisonValue) throws Exception {
        log.error("获取MchDataBean————————默认方法；参数：{}——{}", appId, comparisonValue);
        return giveWeiXinAttributeItem(appId).giveMchDataBeanList().get(0);
    }

    /**
     * 功能描述：获取商户信息
     *
     * @param appId           标识
     * @param comparisonValue 比较值
     * @throws Exception 处理异常
     * @date : 2022/9/24 0024 下午 3:18
     */
    @Override
    public <D> IWeiXinAttributeItem.MchDataBean giveWithdrawMchData(String appId, D comparisonValue) throws Exception {
        log.error("获取MchDataBean————————默认方法；参数：{}——{}", appId, comparisonValue);
        return giveWeiXinAttributeItem(appId).giveWithdrawMchDataBeanList().get(0);
    }

    /**
     * 功能描述：根据AppId获取Mch信息
     * <br />
     *
     * @param mchId 支付商户号
     * @throws Exception 处理异常
     * @author : 王雷
     * @date : 2021/1/21 0021 上午 11:37
     */
    @Override
    public IWeiXinAttributeItem.MchDataBean giveWeiXinPayMchDataBean(String mchId) throws Exception {
        return iWeiXinAttributeStore.giveWeiXinPayMchDataBean(mchId);
    }

    /**
     * 功能描述：根据AppId获取Mch信息
     *
     * @param appId 标识
     * @param mchId 商户号
     * @throws Exception 处理异常
     * @author : 王雷
     * @date : 2021/1/21 0021 上午 11:37
     */
    @Override
    public IWeiXinAttributeItem.MchDataBean giveMchDataByAppIdByRefundToMap(String appId, String mchId) throws Exception {
        log.error("获取MchDataBean（商户号）————————默认方法；参数：{}——{}", appId, mchId);
        return giveWeiXinAttributeItem(appId).giveMchDataBeanList().stream().collect(Collectors
                .toMap(IWeiXinAttributeItem.MchDataBean::getMchId, mchDataBean -> mchDataBean)).get(mchId);
    }

    /**
     * 功能描述：根据AppId获取Mch信息
     *
     * @param comparisonValue 比较值
     * @throws Exception 处理异常
     * @author : 王雷
     * @date : 2021/1/21 0021 上午 11:37
     */
    @Override
    public <D> IWeiXinAttributeItem.MchDataBean giveMchDataToDefault(D comparisonValue) throws Exception {
        return giveWeiXinAttributeItem().giveMchDataBeanList().get(0);
    }

    /**
     * 功能描述：根据AppId获取Mch信息
     *
     * @param appId 标识
     * @throws Exception 处理异常
     * @author : 王雷
     * @date : 2021/1/21 0021 上午 11:37
     */
    @Override
    public Map<String, IWeiXinAttributeItem.MchDataBean> giveMchDataByAppIdToMap(String appId) throws Exception {
        return giveWeiXinAttributeItem(appId).giveMchDataBeanList().stream().collect(Collectors
                .toMap(IWeiXinAttributeItem.MchDataBean::getMchId, mchDataBean -> mchDataBean));
    }

    /**
     * 功能描述：创建Nonces信息
     *
     * @date : 2022/6/29 0029 下午 7:39
     */
    protected String createNonceStr() {
        return WeiXinToolUtil.factory().giveCreateNonceStr();
    }

    /**
     * 功能描述：获得微信签名方法
     *
     * @date : 2022/6/29 0029 下午 8:15
     */
    @Override
    public String dataSignManager(Map<String, Object> params, String appkey) {
        return WeiXinToolUtil.factory().dataSign(params, appkey);
    }

    /**
     * 功能描述：获得微信签名方法
     *
     * @param params          签名内容
     * @param appId           标识
     * @param comparisonValue 比较值
     * @date : 2022/6/29 0029 下午 8:15
     */
    @Override
    public <D> String dataSignManager(Map<String, Object> params, String appId, D comparisonValue) throws Exception {
        return dataSignManager(params, giveMchDataByAppIdToMap(appId, comparisonValue).getPayMchSecret());
    }

    /**
     * 功能描述：map转成xml
     *
     * @date : 2022/6/29 0029 下午 7:56
     */
    protected String mapToXmlWith(Map<String, Object> data) throws Exception {
        return WeiXinToolUtil.factory().mapToXmlWith(data);
    }

    /**
     * 功能描述：字符串转XML
     *
     * @param xml 需要转换的字符串
     * @return 处理结果
     * @date : 2022/7/5 0005 下午 2:01
     */
    protected Map<String, String> strToXmlParse(String xml) throws Exception {
        return WeiXinToolUtil.factory().strToXmlParse(xml);
    }

    /**
     * 功能描述：获得微信签名方法
     *
     * @param managerData                需要处理的数据
     * @param encryptionKey              加密Key
     * @param weiXinTokenManagerFunction 处理回调函数
     * @param size                       随机数长度
     * @param expiration                 失效时间；单位：秒
     * @param secret                     secret标识
     * @date : 2022/6/29 0029 下午 8:15
     */
    @Override
    public <T, D> T giveDataToken(D managerData, int size, String encryptionKey
            , WeiXinTokenManagerFunction<T> weiXinTokenManagerFunction, Long expiration, String secret) throws Exception {
        String jwtrandomKey = WeiXinToolUtil.factory().giveRandomString(size);
        String jwttoken = WeiXinToolUtil.factory().generateToken(managerData, jwtrandomKey, encryptionKey, expiration, secret);
        return weiXinTokenManagerFunction.giveToken(jwtrandomKey, jwttoken);
    }

    /**
     * 功能描述：微信用户信息解密处理
     *
     * @param encryptedData 加密数据
     * @param sessionKey    会话密钥
     * @param ivData        微信IV数据
     * @return 解密后的结果
     * @date : 2022/7/5 0005 下午 3:24
     */
    @Override
    public JSONObject wxUserInformationDecryptionProcessing(String encryptedData, String sessionKey, String ivData) throws Exception {
        return JSONObject.fromObject(giveUserInformationDecryption(encryptedData, sessionKey, ivData));
    }

    protected final static String INSTANCE_KEY = "AES/CBC/PKCS7Padding";
    protected final static String INSTANCE_VALUE = "BC";
    protected final static String INSTANCE_ALGORITHM = "AES";

    /**
     * 功能描述：顾客信息解密处理
     *
     * @param encryptedData 加密数据
     * @param sessionKey    会话密钥
     * @param ivData        微信IV数据
     * @date : 2022/7/6 0006 下午 2:38
     */
    protected String giveUserInformationDecryption(String encryptedData, String sessionKey, String ivData) throws Exception {
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.decode(ivData);
        // 如果密钥不足16位，那么就补足. 这个if 中的内容很重要
        int base = 16;
        if (keyByte.length % base != 0) {
            int groups = keyByte.length / base + 1;
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
            keyByte = temp;
        }
        // 初始化
        Security.addProvider(new BouncyCastleProvider());
        Cipher cipher = Cipher.getInstance(INSTANCE_KEY, INSTANCE_VALUE);
        SecretKeySpec spec = new SecretKeySpec(keyByte, INSTANCE_ALGORITHM);
        AlgorithmParameters parameters = AlgorithmParameters.getInstance(INSTANCE_ALGORITHM);
        parameters.init(new IvParameterSpec(ivByte));
        // 初始化
        cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
        byte[] resultByte = cipher.doFinal(dataByte);
        if (null != resultByte && resultByte.length > 0) {
            String result = new String(resultByte, StandardCharsets.UTF_8);
            log.debug("微信解密——处理结果：{}", result);
            return result;
        }
        throw new NullPointerException("没有可用的数据");
    }

    /**
     * 功能描述：获取Token与Ticket
     *
     * @param appId 标识
     * @return 返回的数据结果；
     * @date : 2022/7/23 0023 上午 11:10
     */
    @Override
    public Map<String, String> giveTokenAndTicket(String appId) throws Exception {
        return new HashMap<String, String>(16) {
            private static final long serialVersionUID = -5708275460174676252L;

            {
                put("token", giveToken(appId));
                put("ticket", giveTicket(appId));
            }
        };
    }

    /**
     * 功能描述：获取Token与Ticket
     *
     * @return 返回的数据结果；
     * @date : 2022/7/23 0023 上午 11:10
     */
    @Override
    public Map<String, String> giveTokenAndTicket() throws Exception {
        return new HashMap<String, String>(16) {
            private static final long serialVersionUID = -5708275460174676252L;

            {
                put("token", giveDefaultToken());
                put("ticket", giveDefaultTicket());
            }
        };
    }


    /**
     * 功能描述：交互参数统一设置AppId处理
     *
     * @param appId     标识
     * @param parameter 参数对象
     * @date : 2022/8/3 0003 下午 7:32
     */
    protected void httpAlternatelyParameterPutTokenManager(MultiValueMap<String, String> parameter, String appId) throws Exception {
        parameter.add("access_token", giveToken(appId));
    }
}
